﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Kyoh.Lib.Excel
{
    /// <summary>Excel COM オブジェクトのラッパーの基本クラスを定義します。</summary>
	public abstract class Component : IDisposable
	{
		private object obj;
		private Component parent;

		private List<Component> mChildren = new List<Component>();

        /// <summary>内包するExcel COM オブジェクトを取得します。</summary>
		internal object ExcelComObject { get { return obj; } }
        /// <summary>親となる <see cref="Component"/> オブジェクトを取得します。</summary>
		internal Component ParentComponent { get { return parent; } }

        /// <summary>Excel COM オブジェクトで、新しいインスタンスを初期化します。</summary>
        /// <param name="obj">Excel COM オブジェクト</param>
        /// <param name="parent">親となる <see cref="Component"/> オブジェクト。</param>
		protected Component(object obj, Component parent)
		{
            this.obj = obj;
            this.parent = parent;
            if (parent != null)
            {
                if (parent.Disposed)
                    throw new ObjectDisposedException("parent");
                parent.mChildren.Add(this);
            }
            this.Disposed = false;
		}

        /// <summary>内包するExcel COM オブジェクトの名前を取得します。</summary>
        protected string GetComClassName
        {
            get { return Reflect.GetComClassName(this.obj); }
        }
		
        /// <summary>COM オブジェクトのメソッドをコールします。</summary>
        /// <param name="methodName">メソッドの名前</param>
        /// <param name="arguments">メソッドの引数</param>
        /// <returns>コールしたメソッドの戻り値。</returns>
		public object InvokeMethod(string methodName, params object[] arguments)
		{
            if (this.Disposed)
				throw new ObjectDisposedException("this");
            return Reflect.InvokeMethod(this.obj, methodName, arguments);
		}

        /// <summary>COM オブジェクトのプロパティを取得します。</summary>
        /// <param name="propertyName">プロパティ名</param>
        /// <param name="arguments">プロパティの引数</param>
        /// <returns>COM オブジェクトのプロパティ。</returns>
        public object GetProperty(string propertyName, params object[] arguments)
		{
            if (this.Disposed)
                throw new ObjectDisposedException("this");
            return Reflect.GetProperty(this.obj, propertyName, arguments);
		}

        /// <summary>COM オブジェクトのプロパティを設定します。</summary>
        /// <param name="propertyName">プロパティ名</param>
        /// <param name="arguments">プロパティの引数</param>
        public void SetProperty(string propertyName, params object[] arguments)
		{
			if (this.Disposed)
                throw new ObjectDisposedException("this");
            Reflect.SetProperty(this.obj, propertyName, arguments);
		}

        /// <summary>現在の <see cref="Component"/> インスタンスを破棄します。</summary>
		public void Dispose()
		{
			Dispose(true);
		}

        /// <summary>ファイナライザ</summary>
		~Component()
		{
			Dispose(false);
		}

        /// <summary>現在の <see cref="Component"/> インスタンスが、破棄されたオブジェクトであるかどうかを取得します。</summary>
		public bool Disposed { get; private set; }

        /// <summary>現在の <see cref="Component"/> インスタンスを破棄します。</summary>
        /// <param name="disposing">明示的な破棄を行う場合は <c>true</c> 、それ以外の場合は <c>false</c> 。</param>
		protected virtual void Dispose(bool disposing)
		{
            if (disposing && !this.Disposed)
            {
                foreach (var child in mChildren)
                    child.Dispose(disposing);
                Marshal.FinalReleaseComObject(this.obj);
            }
            this.Disposed = true;
		}
	}
}